package com.randi.framework.handler;

import com.randi.common.core.domain.Result;
import com.randi.common.enums.ResultCode;
import com.randi.common.exception.BaseException;
import com.randi.common.exception.ServiceException;
import com.randi.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingRequestHeaderException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.http.HttpServletRequest;
import java.sql.SQLException;
import java.sql.SQLSyntaxErrorException;
import java.util.Objects;
import java.util.logging.Handler;

/**
 * @author ziye
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 字符串匹配占位符
     */
    private static final String STRING_FORMAT_PLACEHOLDER = "{}";

    /**
     * 系统异常
     *
     * @param e 错误信息集合
     * @return 错误信息
     */
    @ExceptionHandler(Exception.class)
    public Result doException(Exception e) {
        e.printStackTrace();
        return Result.error(ResultCode.SYSTEM_ERROR);
    }


    /**
     * 请求头参数缺失拦截处理
     *
     * @param e 错误信息集合
     * @return 错误信息
     */
    @ResponseBody
    @ExceptionHandler(MissingRequestHeaderException.class)
    public Result handleMissingRequestHeaderException(MissingRequestHeaderException e) {
        return Result.error(ResultCode.HEADER_PARAM_ERROR);
    }

    /**
     * 请求方式不支持
     *
     * @param e 错误信息集合
     * @return 错误信息
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result handleHttpRequestMethodNotSupportedException(Exception e) {
        return Result.error(ResultCode.REQUEST_METHOD_ERROR);
    }

    /**
     * 请求路径中缺少必需的路径变量
     *
     * @param e       错误信息集合
     * @param request request
     * @return 错误信息
     */
    @ResponseBody
    @ExceptionHandler(MissingPathVariableException.class)
    public Result handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e);
        return Result.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
    }

    /**
     * 请求参数中缺少必需的ServletRequest参数
     *
     * @param e 错误信息集合
     * @return 错误信息
     */
    @ResponseBody
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        e.printStackTrace();
        return Result.error(ResultCode.PARAM_ERROR_MISSING);
    }

    /**
     * 空指针异常
     *
     * @param e 错误信息集合
     * @return 错误信息
     */
    @ExceptionHandler(NullPointerException.class)
    public Result handleNullPointerException(NullPointerException e) {
        e.printStackTrace();
        return Result.error(ResultCode.SYSTEM_ERROR);
    }

    /**
     * 校验错误拦截处理
     *
     * @param e 错误信息集合
     * @return 错误信息
     */
    @ResponseBody
    @ExceptionHandler(BindException.class)
    public Result handleMethodArgumentBindException(BindException e) {
        String baseMessage = "参数校验错误";
        String message = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
        if (StringUtils.isNotEmpty(message)) {
            baseMessage += ", " + message;
        }
        return Result.error(ResultCode.PARAM_ERROR.status(), baseMessage);
    }

    /**
     * 校验错误拦截处理
     *
     * @param e 错误信息集合
     * @return 错误信息
     */
    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        String baseMessage = "参数错误";
        String message = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
        if (StringUtils.isNotEmpty(message)) {
            baseMessage += ", " + message;
        }
        return Result.error(ResultCode.PARAM_ERROR.status(), baseMessage);
    }

    /**
     * sql异常捕获
     *
     * @param e 错误信息集合
     * @return 错误信息
     */
    @ResponseBody
    @ExceptionHandler(SQLSyntaxErrorException.class)
    public Result handleSqlSyntaxErrorException(SQLSyntaxErrorException e) {
        e.printStackTrace();
        return Result.error(ResultCode.SQL_ERROR);
    }

    /**
     * 唯一键冲突异常
     *
     * @param e DuplicateKeyException
     * @return 错误信息
     * @author ziye
     */
    @ResponseBody
    @ExceptionHandler(DuplicateKeyException.class)
    public Result handelDuplicateKeyException(DuplicateKeyException e) {
        e.printStackTrace();
        final String entryName = e.getCause().getMessage().split("'")[3];
        if (StringUtils.isNotEmpty(entryName)) {
            ResultCode resultCode = ResultCode.PARAM_ERROR_DUPLICATE_FORMAT;
            return Result.error(resultCode.status(), StringUtils.format(resultCode.message(), entryName));
        }
        return Result.error(ResultCode.PARAM_ERROR_DUPLICATE);
    }

    /**
     * 业务异常
     *
     * @param e 业务错误信息集合
     * @return 错误信息
     */
    @ResponseBody
    @ExceptionHandler(ServiceException.class)
    public Result handleServiceException(ServiceException e) {
        String detailMessage = e.getDetailMessage();
        if (StringUtils.isNotEmpty(detailMessage)) {
            String message = e.getMessage();
            if (StringUtils.contains(message, STRING_FORMAT_PLACEHOLDER)) {
                detailMessage = StringUtils.format(message, detailMessage);
            } else {
                detailMessage = StringUtils.format("{}, {}", message, detailMessage);
            }
            return Result.error(e.getCode(), detailMessage);
        }
        return Result.error(e.getCode(), e.getMessage());
    }


    /**
     * 拦截未知的运行时异常
     *
     * @param e 业务错误信息集合
     * @return 错误信息
     */
    @ResponseBody
    @ExceptionHandler(RuntimeException.class)
    public Result handleRuntimeException(BaseException e) {
        e.printStackTrace();
        return Result.error(e.getCode(), e.getMessage());
    }


    /**
     * 权限校验异常
     */
    @ResponseBody
    @ExceptionHandler(AccessDeniedException.class)
    public Result handleAccessDeniedException(AccessDeniedException e) {
        return Result.error(ResultCode.NO_PERMISSION);
    }

    /**
     * Redis连接异常
     */
    @ResponseBody
    @ExceptionHandler(RedisConnectionFailureException.class)
    public Result handleRedisConnectionFailureException(RedisConnectionFailureException e) {
        return Result.error(ResultCode.REDIS_CONNECTION_ERROR);
    }

    /**
     * 数据库连接异常
     */
    @ResponseBody
    @ExceptionHandler(SQLException.class)
    public Result handleCommunicationsException(SQLException e) {
        return Result.error(ResultCode.SQL_ERROR);
    }

}
